#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "RMM_Lib.h"
#include "SMM_Lib.h"
#include "HF_Defines.h"
#include "BF_Defines.h"
ScanMatrix Scans[MAXSCANS];

void SMM_Init(){
	int i; 
	for( i = 0; i < MAXSCANS; i++){
		Scans[i].fileDesc = -1;
	}
}

BOOLEAN FileDescIsValid ( int fileDesc){
	if(fileDesc >= 0 && fileDesc < MAXOPENFILES){
		return TRUE;
	}
	return FALSE;
}
BOOLEAN RecordSizeIsValid( int recordSize){
	if(recordSize > 0 && recordSize < BF_BLOCK_SIZE){
		return TRUE;
	}
	return FALSE;
}

BOOLEAN ScanParametersAreValid(char attrType, int attrLength, int attrOffset, int op, int recordSize){
	
	if( !(attrType == 'c' || attrType == 'f' || attrType == 'i') ){
		return FALSE;
	} 
	if( attrLength < 1 || attrLength > 255){
		return FALSE;
	}
	if(attrOffset < 0 || attrOffset > recordSize){
		return FALSE;
	}
	if (op < 0 || op > 6){
		return FALSE;
	}
	return TRUE;
}

int SMM_GetEmptySpot(){
	int i;
	for(i = 0; i< MAXSCANS; i++){
		if(Scans[i].fileDesc == -1){
			return i;
		}
	}
	return MAXSCANS;		//ean o pinakas einai gematos epistrefei MAXSCANS
}



int SMM_OpenFileScan(int fileDesc, int recordSize,char attrType, int attrLength, int attrOffset, int op, char * value ){
	int empty_spot;
	if( !FileDescIsValid(fileDesc) ) {
		return HF_errno = HFE_BFERROR;
	}
	if( !RecordSizeIsValid( recordSize ) ){
		return HF_errno = INVALID_RECORD_SIZE;
	}
	if (value!=NULL){
		if( !ScanParametersAreValid( attrType,  attrLength,  attrOffset,  op, recordSize) ){
			return HF_errno = INVALID_PARAMETERS;		
		}
	}
	if( (empty_spot = SMM_GetEmptySpot()) == MAXSCANS){		//ean to empty eiani MAXSCANS, den exei keni thesi
		return HF_errno = HFE_STFULL;
	}
	
	Scans[empty_spot].op = op;
	Scans[empty_spot].attrtype = attrType;
	Scans[empty_spot].offset = attrOffset;
	Scans[empty_spot].attrlength = attrLength;
	Scans[empty_spot].fileDesc = fileDesc;
	Scans[empty_spot].lastRecAccessed = -1;
	Scans[empty_spot].recordSize = recordSize;
	if (value!=NULL){
		Scans[empty_spot].value = malloc(attrLength*sizeof(char));
		memcpy(Scans[empty_spot].value, value, attrLength);
	}
	else
		Scans[empty_spot].value=NULL;
	return empty_spot;
	
}


BOOLEAN ScanDescIsValid(int scanDesc){
	if(scanDesc < 0 || scanDesc > MAXSCANS){
		return FALSE;
	}
	if(Scans[scanDesc].fileDesc == -1){
		return FALSE;
	}
	return TRUE;
}

BOOLEAN SMM_Match(char * record,int scanDesc){
	if ( Scans[scanDesc].value == NULL )
		return TRUE;
	char * attribute;
	int attri;
	float attr;
	float float_value; //= atof(Scans[scanDesc].value);
	int int_value; //= atoi(Scans[scanDesc].value);
	memcpy ((char*) &int_value, Scans[scanDesc].value, 4);
	//printf("%d\n", int_value);
	memcpy ((char*) &float_value, Scans[scanDesc].value, 4);
	char * char_value;
	char_value = malloc(Scans[scanDesc].attrlength);
	memcpy(char_value, Scans[scanDesc].value, Scans[scanDesc].attrlength);
	char * ptr;
	ptr = record;
	ptr+= Scans[scanDesc].offset;
	int type;
	
	if(Scans[scanDesc].attrtype == 'c'){
		type = 1;
		attribute = malloc(Scans[scanDesc].attrlength + 1);
		memcpy(attribute, ptr, Scans[scanDesc].attrlength + 1);	//antegrapse apo ekeino to byte k meta, gia lenght byte
	}
	else if(Scans[scanDesc].attrtype == 'i'){
		type = 2;
		memcpy((char*) &attri, ptr, 4);	
		//printf("%d\n", attri);	
	}
	else{
		type = 3;
		memcpy((char*) &attr, ptr, 4);	
	}	
							 	
	switch (type ) {
		case 1:
			switch (Scans[scanDesc].op) {
				case EQUAL:
					return (strncmp(attribute,char_value,Scans[scanDesc].attrlength)==0);
					break;
				case LESS_THAN:
					return (strncmp(attribute,char_value,Scans[scanDesc].attrlength)<0);
					break;
				case GREATER_THAN:
					return (strncmp(attribute,char_value,Scans[scanDesc].attrlength)>0);
					break;
				case GREATER_THAN_OR_EQUAL:
					return (strncmp(attribute,char_value,Scans[scanDesc].attrlength)>=0);
					break;
				case LESS_THAN_OR_EQUAL:
					return (strncmp(attribute,char_value,Scans[scanDesc].attrlength)<=0);
					break;
				case NOT_EQUAL:
					return (strncmp(attribute,char_value,Scans[scanDesc].attrlength)!=0);
					break;
				default:
					break;
			}
			
		case 2:	//tha einai h c h arithmos ( ta float kai int exoun tin idia metaxirisi ) 
			switch (Scans[scanDesc].op) {
				case EQUAL:
					return (attri == int_value);
					break;
				case LESS_THAN:
					return (attri < int_value);
					break;
				case GREATER_THAN:
					return(attri > int_value);
					break;
				case GREATER_THAN_OR_EQUAL:
					return(attri >= int_value);
					break;
				case LESS_THAN_OR_EQUAL:
					return(attri <= int_value);
					break;
				case NOT_EQUAL:
					return(attri != int_value);
					break;

			}
		case 3:	//tha einai h c h arithmos ( ta float kai int exoun tin idia metaxirisi ) 
			switch (Scans[scanDesc].op) {
				case EQUAL:
					return (attr == float_value);
					break;
				case LESS_THAN:
					return (attr < float_value);
					break;
				case GREATER_THAN:
					return(attr > float_value);
					break;
				case GREATER_THAN_OR_EQUAL:
					return(attr >= float_value);
					break;
				case LESS_THAN_OR_EQUAL:
					return(attr <= float_value);
					break;
				case NOT_EQUAL:
					return(attr != float_value);
					break;
					
			}
			
			break;
	}
	return FALSE;
}

int SMM_FindNextRec(int scanDesc, char * record){
	int recId;
	if( !ScanDescIsValid(scanDesc) ){
		return HF_errno = INVALID_SD;
	}
	if( Scans[scanDesc].lastRecAccessed == -1 && Scans[scanDesc].fileDesc!= -1){		//ean eiani i prwti fora pou prospelaunoume to arxeio
		recId = RMM_GetFirstRec(Scans[scanDesc].fileDesc, record, Scans[scanDesc].recordSize);
		if( recId < 0){				//fernei to proto block. Ean ayto apotuxei, error
			return HF_errno = recId;
		}
		else{		//alliws bazei ston pinaka oti to teleutaio pou prospelastike itan to rec id
			Scans[scanDesc].lastRecAccessed = recId;
		}
	}
	else{
		recId = RMM_GetNextRec(Scans[scanDesc].fileDesc, Scans[scanDesc].lastRecAccessed, record, Scans[scanDesc].recordSize);
		if(recId < 0){		//an to arxeio eixe idi prospalstei, fernoume to epomeno egiro rec
			return HF_errno = recId;
		}
		else{		//an auto petuxei, anathetoume sto last rec accesed to kainourio recId
			Scans[scanDesc].lastRecAccessed = recId;
		}
	}
	while( !SMM_Match(record,scanDesc) ){	//oso den tairiazei tin perigrafi mas
		recId = RMM_GetNextRec(Scans[scanDesc].fileDesc, Scans[scanDesc].lastRecAccessed, record, Scans[scanDesc].recordSize);
		if(recId < 0){		//fernei tin epomeni egiri egrafi
			return HF_errno = recId;		//ean auto apotuxei (telos tou arxeio) epistrefoume error
		}
		else{
			Scans[scanDesc].lastRecAccessed = recId;
		}
	}
	
	HF_errno = HFE_OK;
	return recId;
}


/*int SMM_FindNextRec(int scanDesc, char * record){
	
	if( !ScanDescIsValid(scanDesc) ){
		return HF_errno = INVALID_SD;
	}
	if( Scans[scanDesc].lastRecAccessed == -1 ){
		if( (HF_errno = RMM_GetFirstRec(Scans[scanDesc].fileDesc, record, Scans[scanDesc].recordSize )) < 0){
			return HF_errno;
		}
		Scans[scanDesc].lastRecAccessed++;
		if(SMM_Match(record, scanDesc) ){
			return Scans[scanDesc].lastRecAccessed;
		}
	}
	else{
		HF_errno = RMM_GetThisRec(Scans[scanDesc].fileDesc, Scans[scanDesc].lastRecAccessed + 1, record, Scans[scanDesc].recordSize); 
	}
	
	while(HF_errno < 0 || !SMM_Match(record,scanDesc) ){
		Scans[scanDesc].lastRecAccessed++;
		HF_errno = RMM_GetThisRec(Scans[scanDesc].fileDesc, Scans[scanDesc].lastRecAccessed + 1, record, Scans[scanDesc].recordSize); 
		
	} 
	return ++Scans[scanDesc].lastRecAccessed;
	
}*/


int SMM_CloseFileScan(int scanDesc){
	
	if(!ScanDescIsValid(scanDesc) ){
		return INVALID_SD;
	}
	Scans[scanDesc].fileDesc = -1;
	free(Scans[scanDesc].value);
}

BOOLEAN SMM_hasOpenScans(int fileDesc){
	int i;
	/*if(! FileDescIsValid(fileDesc)){
		return INVALID_FD;
	}*/
	for(i = 0; i< MAXSCANS; i++){
		if(Scans[i].fileDesc == fileDesc ){
			return TRUE;
		}
	}
	return FALSE;
}


